// +FHDR------------------------------------------------------------
//                 Copyright (c) 2024 NOVAUTO.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : sync_hand_fifo_ram_delay2.v
// Author        : ICer
// Created On    : 2024-01-04 11:18
// Last Modified : 2024-01-07 00:12 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------


module sync_hand_fifo_ram_delay2 #(
    //parameter
    parameter WIDTH = 8,
    parameter DEPTH = 8
)( /*AUTOARG*/
   // Outputs
   data_in_ready, data_out_valid, data_out,
   // Inputs
   clk, rst_n, data_in_valid, data_in, data_out_ready
   );

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input             clk;
input             rst_n;

input             data_in_valid;
input [WIDTH -1:0]data_in;
output            data_in_ready;

output            data_out_valid;
output[WIDTH -1:0]data_out;
input             data_out_ready;

// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
//parameter declare
localparam ADDR_W     = $clog2(DEPTH);
localparam ADDR_W_EX  = ADDR_W + 1;
localparam DEPTH_W    = $clog2(DEPTH);
localparam DEPTH_W_EX = DEPTH_W + 1;

//wire declare
reg  [ADDR_W_EX -1:0]fifo_cnt_in;
reg  [ADDR_W_EX -1:0]fifo_cnt_out;

reg  [ADDR_W    -1:0]waddr;
wire [ADDR_W    -1:0]raddr;
wire [WIDTH     -1:0]wdata;
wire [WIDTH     -1:0]rdata;
wire                 wenc;
wire                 renc = 1'b1;

//wire pre-logic
wire data_in_hand_en  = (data_in_valid && data_in_ready);
wire data_out_hand_en = (data_out_valid && data_out_ready);

// ----------------------------------------------------------------
// AUTO declare
// ----------------------------------------------------------------
/*AUTOOUTPUT*/
/*AUTOINPUT*/
/*AUTOWIRE*/

// ----------------------------------------------------------------
// ram inst
// ----------------------------------------------------------------
/*dual_port_ram AUTO_TEMPLATE(
.wclk(clk),
.rclk(clk),
.DEPTH(DEPTH),
);
*/
dual_port_ram #(/*AUTOINSTPARAM*/
                // Parameters
                .DEPTH                  (DEPTH),                 // Templated
                .WIDTH                  (WIDTH))
u_ram(
      .rdata                            (rdata[WIDTH-1:0]),
      /*AUTOINST*/
      // Inputs
      .wclk                             (clk),                   // Templated
      .wenc                             (wenc),
      .waddr                            (waddr[$clog2(DEPTH)-1:0]),
      .wdata                            (wdata[WIDTH-1:0]),
      .rclk                             (clk),                   // Templated
      .renc                             (renc),
      .raddr                            (raddr[$clog2(DEPTH)-1:0]));

// ----------------------------------------------------------------
// fifo_cnt_in and fifo_out_cnt
// ----------------------------------------------------------------
reg wenc_ff;
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    wenc_ff <= 1'b0;
  end 
  else begin
    wenc_ff <= wenc;
  end
end

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    fifo_cnt_in <= {ADDR_W_EX{1'b0}};
  end 
  else if(wenc ^ data_out_hand_en) begin
    if(wenc) fifo_cnt_in <= fifo_cnt_in + 1'b1;
    else     fifo_cnt_in <= fifo_cnt_in - 1'b1;
  end
end

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    fifo_cnt_out <= {ADDR_W_EX{1'b0}};
  end 
  else if(wenc_ff ^ data_out_hand_en) begin
    if(wenc_ff) fifo_cnt_out <= fifo_cnt_in + 1'b1;
    else        fifo_cnt_out <= fifo_cnt_in - 1'b1;
  end
end

// ----------------------------------------------------------------
// wenc/waddr
// ----------------------------------------------------------------
assign wenc = data_in_hand_en;
always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    waddr <= {ADDR_W{1'b0}};
  end 
  else if(wenc) begin
    if(waddr >= DEPTH - 1'b1) waddr <= {ADDR_W{1'b0}};
    else                      waddr <= waddr + 1'b1;
  end
end

assign wdata = data_in;

// ----------------------------------------------------------------
// renc/raddr
// ----------------------------------------------------------------
assign renc = 1'b1;

reg [ADDR_W -1:0]raddr_q;
wire[ADDR_W -1:0]raddr_d;

assign raddr_d = (raddr_q >= DEPTH - 1'b1) ? {ADDR_W{1'b0}} : raddr_q + 1'b1;
assign raddr   = data_out_hand_en ? raddr_d : raddr_q;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    raddr_q <= {ADDR_W{1'b0}};
  end 
  else if(data_out_hand_en) begin
    raddr_q <= raddr_d;
  end
end

// ----------------------------------------------------------------
// out logic
// ----------------------------------------------------------------
assign data_in_ready  = (fifo_cnt_in < DEPTH);
assign data_out_valid = (fifo_cnt_out > {ADDR_W_EX{1'b0}});
assign data_out       = rdata;

endmodule
// Local Variables:
// verilog-auto-inst-param-value:t
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:

